package com.dbflow5.sqlcipher;

import com.dbflow5.DatabaseFileUtils;
import com.dbflow5.config.DBFlowDatabase;
import com.dbflow5.config.DatabaseConfig;
import com.dbflow5.database.*;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteOpenHelper;
import ohos.app.Context;

/**
 * Description: The replacement [OpenHelper] for SQLCipher. Specify a subclass of this is [DatabaseConfig.getDatabaseClass]
 * of your database to get it to work with specifying the secret you use for the databaseForTable.
 */
public abstract class SQLCipherOpenHelper extends SQLiteOpenHelper implements OpenHelper {

    private final Context context;

    public LocalDatabaseHelperDelegate delegate;
    private SQLCipherDatabase cipherDatabase = null;
    private final String _databaseName;

    public SQLCipherOpenHelper(Context context, DBFlowDatabase databaseDefinition, DatabaseCallback listener) {
        super(context, databaseDefinition.isInMemory()? null : context.getExternalCacheDir() + "/" + databaseDefinition.getDatabaseFileName(), null, databaseDefinition.databaseVersion());
        this.context = context;
        _databaseName = databaseDefinition.getDatabaseFileName();

        SQLiteDatabase.loadLibs(context);
        if(delegate == null) {
            delegate = new LocalDatabaseHelperDelegate(context, listener, databaseDefinition, databaseDefinition.backupEnabled()?
                    // Temp database mirrors existing
                    new BackupHelper(context, LocalDatabaseHelperDelegate.getTempDbFileName(databaseDefinition), databaseDefinition.databaseVersion(), databaseDefinition) : null);
        }
    }

    public LocalDatabaseHelperDelegate delegate() {
        return delegate;
    }

    @Override
    public boolean isDatabaseIntegrityOk() {
        return delegate.isDatabaseIntegrityOk();
    }

    @Override
    public DatabaseWrapper database() {
        if (cipherDatabase == null || !cipherDatabase.database.isOpen()) {
            cipherDatabase = SQLCipherDatabase.from(getWritableDatabase(cipherSecret()));
        }
        return cipherDatabase;
    }

    protected abstract String cipherSecret();

    @Override
    public void performRestoreFromBackup() {
        delegate.performRestoreFromBackup();
    }

    @Override
    public void backupDB() {
        delegate.backupDB();
    }

    @Override
    public void setDatabaseListener(DatabaseCallback callback) {
        delegate.setDatabaseHelperListener(callback);
    }

    @Override
    public void onConfigure(SQLiteDatabase db) {
        delegate.onConfigure(SQLCipherDatabase.from(db));
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        delegate.onCreate(SQLCipherDatabase.from(db));
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        delegate.onUpgrade(SQLCipherDatabase.from(db), oldVersion, newVersion);
    }

    @Override
    public void onOpen(SQLiteDatabase db) {
        delegate.onOpen(SQLCipherDatabase.from(db));
    }

    @Override
    public void setWriteAheadLoggingEnabled(boolean enabled) {
        if(cipherDatabase != null && cipherDatabase.database != null) {
            if (enabled) {
                cipherDatabase.database.enableWriteAheadLogging();
            } else {
                cipherDatabase.database.disableWriteAheadLogging();
            }
        }
    }

    @Override
    public void closeDB() {
        database();
        if(cipherDatabase != null && cipherDatabase.database != null) {
            cipherDatabase.database.close();
        }
    }

    @Override
    public void deleteDB() {
        DatabaseFileUtils.deleteDatabase(context, _databaseName);
    }

    /**
     * Simple helper to manage backup.
     */
    private class BackupHelper extends SQLiteOpenHelper implements OpenHelper {
        private final Context context;
        private SQLCipherDatabase sqlCipherDatabase = null;
        private final LocalDatabaseHelper databaseHelper;
        private final String _databaseName;

        public BackupHelper(Context context,String name, int version, DBFlowDatabase databaseDefinition) {
            super(context, name, null, version);
            this.context = context;
            databaseHelper = new LocalDatabaseHelper(new OhosMigrationFileHelper(context), databaseDefinition);
            _databaseName = databaseDefinition.getDatabaseFileName();
        }

        @Override
        public DatabaseWrapper database() {
            if (sqlCipherDatabase == null) {
                sqlCipherDatabase = SQLCipherDatabase.from(getWritableDatabase(cipherSecret()));
            }
            return sqlCipherDatabase;
        }

        @Override
        public LocalDatabaseHelperDelegate delegate() {
            return null;
        }

        @Override
        public boolean isDatabaseIntegrityOk() {
            return false;
        }

        @Override
        public void performRestoreFromBackup() {
        }

        @Override
        public void backupDB() {
        }

        @Override
        public void closeDB() {
        }

        @Override
        public void setDatabaseListener(DatabaseCallback callback) {
        }

        @Override
        public void onConfigure(SQLiteDatabase db) {
            databaseHelper.onConfigure(SQLCipherDatabase.from(db));
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            databaseHelper.onCreate(SQLCipherDatabase.from(db));
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            databaseHelper.onUpgrade(SQLCipherDatabase.from(db), oldVersion, newVersion);
        }

        @Override
        public void onOpen(SQLiteDatabase db) {
            databaseHelper.onOpen(SQLCipherDatabase.from(db));
        }

        @Override
        public void setWriteAheadLoggingEnabled(boolean enabled) {
        }

        @Override
        public void deleteDB() {
            DatabaseFileUtils.deleteDatabase(context, _databaseName);
        }
    }

    public static DatabaseConfig.OpenHelperCreator createHelperCreator(Context context, String secret) {
        return (db, callback) -> new SQLCipherOpenHelper(context, db, callback){
            @Override
            protected String cipherSecret() {
                return secret;
            }
        };
    }
}
